package com.zenith.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zenith.common.constant.CommonConstant;
import com.zenith.common.constant.RoleConstant;
import com.zenith.common.enumcode.RedisEnum;
import com.zenith.common.result.Result;
import com.zenith.common.util.JwtUtil;
import com.zenith.gateway.config.WhitelistConfig;
import com.zenith.gateway.service.PermissionService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.springframework.context.annotation.Lazy;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private WhitelistConfig whitelistConfig;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    @Lazy
    private PermissionService permissionService;

    @Value("${gateway.rate-limit.enabled:true}")
    private boolean rateLimitEnabled;

    @Value("${gateway.rate-limit.requests-per-minute:60}")
    private int requestsPerMinute;

    @Value("${gateway.ip-blacklist:}")
    private String ipBlacklist;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final Map<String, List<Long>> requestFrequencyMap = new ConcurrentHashMap<>();
    private final Set<String> blacklistedIps = new HashSet<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        String clientIp = getClientIp(request);

        log.debug("处理请求: {} 来自IP: {}", path, clientIp);

        if (isIpBlacklisted(clientIp)) {
            log.warn("黑名单IP尝试访问: {}", clientIp);
            return forbidden(exchange, "访问被拒绝");
        }

        if (isWhitelist(path)) {
            log.debug("白名单路径直接放行: {}", path);
            return chain.filter(exchange);
        }

        if (rateLimitEnabled && isRateLimited(clientIp)) {
            log.warn("请求频率超限: {}", clientIp);
            return tooManyRequests(exchange, "请求过于频繁");
        }

        String token = getToken(request);
        if (!StringUtils.hasText(token)) {
            log.warn("未提供令牌: {}", clientIp);
            return unauthorized(exchange, "未提供令牌");
        }

        Claims claims = JwtUtil.parseToken(token);
        if (claims == null || !JwtUtil.validateToken(token)) {
            log.warn("令牌无效或已过期: {}", clientIp);
            return unauthorized(exchange, "令牌无效或已过期");
        }

        if (isTokenRevoked(token)) {
            log.warn("令牌已被撤销: {}", clientIp);
            return unauthorized(exchange, "令牌已被撤销");
        }

        Long userId = JwtUtil.getUserId(token);
        String username = JwtUtil.getUsername(token);
        String role = JwtUtil.getUserRole(token);

        if (!isUserActive(userId)) {
            log.warn("用户已被禁用: {}", username);
            return forbidden(exchange, "用户已被禁用");
        }

        if (!hasPermission(path, request.getMethod().name(), role)) {
            log.warn("权限不足: 用户 {} 尝试访问 {}", username, path);
            return forbidden(exchange, "权限不足，无法访问该资源");
        }

        ServerHttpRequest newRequest = request.mutate()
                .header(CommonConstant.USER_ID, String.valueOf(userId))
                .header(CommonConstant.USERNAME, username)
                .header(CommonConstant.USER_ROLE, role)
                .header("X-Client-IP", clientIp)
                .build();

        return chain.filter(exchange.mutate().request(newRequest).build());
    }

    private boolean hasPermission(String path, String method, String userRole) {
        if (RoleConstant.ADMIN.equals(userRole)) {
            return true;
        }
        return permissionService.hasPermission(path, method, userRole);
    }

    // 其他辅助方法保持不变...
    private boolean isWhitelist(String path) {
        return whitelistConfig.getWhitelistUrls().stream()
                .anyMatch(pattern -> pathMatcher.match(pattern, path));
    }

    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(CommonConstant.TOKEN_HEADER);
        if (StringUtils.hasText(token) && token.startsWith(CommonConstant.TOKEN_PREFIX)) {
            return token.substring(CommonConstant.TOKEN_PREFIX.length());
        }
        return null;
    }

    private String getClientIp(ServerHttpRequest request) {
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (StringUtils.hasText(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        String xRealIp = request.getHeaders().getFirst("X-Real-IP");
        if (StringUtils.hasText(xRealIp)) {
            return xRealIp;
        }
        return Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
    }

    private boolean isIpBlacklisted(String clientIp) {
        if (!StringUtils.hasText(ipBlacklist)) {
            return false;
        }
        if (blacklistedIps.isEmpty()) {
            String[] ips = ipBlacklist.split(",");
            for (String ip : ips) {
                blacklistedIps.add(ip.trim());
            }
        }
        return blacklistedIps.contains(clientIp);
    }

    private boolean isRateLimited(String clientIp) {
        long currentTime = System.currentTimeMillis();
        List<Long> timestamps = requestFrequencyMap.computeIfAbsent(clientIp, k -> new ArrayList<>());
        timestamps.removeIf(timestamp -> currentTime - timestamp > 60000);
        if (timestamps.size() >= requestsPerMinute) {
            return true;
        }
        timestamps.add(currentTime);
        return false;
    }

    private boolean isTokenRevoked(String token) {
//        return redisUtil.isTokenRevoked(token);
        Object object = redisTemplate.opsForValue().get(RedisEnum.TOKEN_REVOKED.getKey() + token);
        return object != null;
    }

    private boolean isUserActive(Long userId) {
//        String userStatus = redisUtil.get(RedisEnum.USER_STATUS.getKey() + userId).toString();
        String userStatus = redisTemplate.opsForValue().get(RedisEnum.USER_STATUS.getKey() + userId).toString();
        return userStatus == null || "active".equals(userStatus);
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Result<?> result = Result.unauthorized(message);
        byte[] bytes;
        try {
            bytes = objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8);
        } catch (JsonProcessingException e) {
            bytes = "{\"code\":401,\"message\":\"未授权\",\"data\":null}".getBytes(StandardCharsets.UTF_8);
        }
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

    private Mono<Void> forbidden(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Result<?> result = Result.forbidden(message);
        byte[] bytes;
        try {
            bytes = objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8);
        } catch (JsonProcessingException e) {
            bytes = "{\"code\":403,\"message\":\"禁止访问\",\"data\":null}".getBytes(StandardCharsets.UTF_8);
        }
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

    private Mono<Void> tooManyRequests(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Result<?> result = Result.fail(429, message);
        byte[] bytes;
        try {
            bytes = objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8);
        } catch (JsonProcessingException e) {
            bytes = "{\"code\":429,\"message\":\"请求过于频繁\",\"data\":null}".getBytes(StandardCharsets.UTF_8);
        }
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}